#include "common.h"
#include "sock_event.h"

void xevtimer_set(event_t *ev, void (*cb)(int, short, void *), void *data)
{
	evtimer_set(ev, cb, data);
}

int xevtimer_del(event_t *ev)
{
	return evtimer_del(ev);
}

int xevtimer_add(event_t *ev, struct timeval *tv)
{
	return evtimer_add(ev, tv);
}

int xevtimer_update(event_t *ev, struct timeval *tv, void *data, void (*cb)(int, short, void *))
{
	if(!ev)
		return -1;
	xevtimer_del(ev);
	xevtimer_set(ev, cb, data);
	if(xevtimer_add(ev, tv) == -1) 
		return -1;
	return 0;
}

void xevent_log(int sec, const char *msg)
{
	debug_log(5, "EVENT %d, %s\n", sec, msg);
}

event_base_t *xevent_init()
{
	return event_init();
}

int xevent_add(event_t *ev)
{
	if(!ev) {
		debug_log(0, "%s", "ev is null\n");
		return -1;
	}
	if(event_add(ev, NULL) == -1) {
		debug_log(0, "%s", "event add failed\n");
		return -1;
	}
	return 0;
}

int xevent_del(event_t *ev)
{
	if(!ev)
		return -1;
	if(event_del(ev) == -1) {
		debug_log(0, "%s", "event del failed\n");
		return -1;
	}
	return 0;
}

int xevent_update(event_t *ev, short flag, void *data, \
				void (*cb)(int, short, void *))
{
	if(!ev)
		return -1;
	event_base_t *base =  ev->ev_base;
	int fd = ev->ev_fd;
	if(-1 == xevent_del(ev)) {
		debug_log(0, "%s", "event del failed\n");
		return -1;
	}
	xevent_set(ev, fd, flag, data, cb);
	xevent_base_set(ev, base);
	if(xevent_add(ev) == -1) {
		debug_log(0, "%s", "event add failed\n");
		return -1;
	}
	return 0;
}

int xevent_set(event_t *ev, int fd, short flag, void *data, \
				void (*cb) (int, short, void *))
{
	if(!ev) {
		debug_log(0, "%s", "ev is null\n");
		return -1;
	}
	event_set(ev, fd, flag, cb, data);
	return 0;
}

event_t *xevent_new()
{
	event_t *ev = g_slice_new(event_t);
	return ev;
}

extern void xevent_clean(event_t *ev)
{
	if(!ev)
		return;
	xevent_del(ev);
	g_slice_free(event_t, ev);
}

event_base_t * xevent_base_new()
{
	event_base_t *base = NULL;
	
	if((base = event_base_new()) == NULL) {
		debug_log(0, "%s", "failed to alloc event_base");
	}

	return base;
}

void xevent_base_clean(event_base_t *base)
{
	if(!base)
		return ;
	event_base_free(base);
}

extern void xevent_base_set(event_t *ev, event_base_t *base)
{
	if(-1 == event_base_set(base, ev))
		debug_log(0, "%s", "event base set failed\n");
}

void xevent_base_run(event_base_t *base)
{
	debug_log(5, "%s", "event loop\n");
	if(event_base_loop(base, 0) == -1) 
		debug_log(0, "%s", "event base dispatch failed\n");
}

extern void xevent_base_stop(event_base_t *base)
{
}

#if 0
static void event_callback_set(event_t *ev, void *data, \
				void (*cb) (int, short, void *), struct timeval tv);
static void event_timeout_check(event_base_t *base);

static void event_callback_set(event_t *ev, void *data, \
				void (*cb) (int, short, void *), struct timeval tv)
{
	if(!ev)
		return;

	switch(ev->flag) {
		case SOCK_FLAG_READ:
			ev->read_cb = cb;
			ev->read_data = data;
			break;
		case SOCK_FLAG_WRITE:
			ev->write_cb = cb;
			ev->write_data = data;
			break;
		case SOCK_FLAG_TIMEOUT: {
			struct timeval now;
			current_time_get(&now);
			tv.tv_sec += now.tv_sec;
			ev->timeout = tv.tv_sec;
			ev->timeout_cb = cb;
			ev->timeout_data = data;
			break;
			}
		case SOCK_FLAG_NONE:
			debug_log(5, "flag %s\n", "SOCK_FLAG_NONE");
			break;
		default: 
			debug_log(1, "unknown %d flag\n", ev->flag);
			break;
	}
}

int event_set(event_t *ev, int fd, short flag, void *data, \
				void (*cb) (int, short, void *), struct timeval *tv)
{
	if(!ev)
		return -1;
	struct timeval zero = {0, 0};

	ev->fd = fd;
	ev->flag = flag;
	if(tv) 
		zero = *tv;
	event_callback_set(ev, data, cb, zero);
	
	debug_log(9, "fd %d, flag %d, data %p, cb %p, tv %p set\n",
					ev->fd, flag, data, cb, &zero);
	return 0;
}

event_base_t * event_base_new()
{
	return kqueue_event_base_new();
}

void event_base_run(event_base_t *base)
{
	for( ; ;) {
		if(base->stop) {
			debug_log(1, "%s\n", "stop event loop");
			break;
		}
		event_timeout_check(base);
		kqueue_event_base_run(base);
	}
}

static void event_timeout_check(event_base_t *base)
{
	if(!base)
		return;
	
	struct timeval now;
	struct list_head *head = &base->active_list;
	struct list_head *p, *n;

	if(list_empty(head))  {
		debug_log(5, "%p do not have connections\n", base);
		return;
	}
	current_time_get(&now);
	list_for_each_safe(p, n, head) {
		event_t *ev = list_entry(p, event_t, list);

		assert(ev);
		if(ev->timeout <= 0) 
			continue;
		if(ev->timeout <= now.tv_sec) {
			/* timeout event found */
			debug_log(3, "fd %d event timeout %d seconds\n", ev->fd, ev->timeout - now.tv_sec);
			event_del(ev);
		}
	}
}

event_t *event_new(event_base_t *base)
{
	event_t *ev = mempool_alloc(sizeof(*ev));

	if(!ev)
		return NULL;
	INIT_LIST_HEAD(&ev->list);
	ev->timeout = 0;
	ev->base = base;

	return ev;
}

void event_clean(event_t *ev)
{
	if(!ev)
		return;

	ev->flag = SOCK_FLAG_CLOSE;
	event_del(ev);
	mempool_free(ev);
}

void event_base_set(event_t *ev, event_base_t *base)
{
	if(!ev)
		return;

	ev->base = base;
}

int event_add(event_t *ev)
{
	if(!ev)
		return -1;

	list_add_tail(&ev->list, &ev->base->active_list);
	kqueue_event_set(ev);
	return 0;
}

int event_del(event_t *ev)
{
	if(!ev)
		return -1;

	list_del_init(&ev->list);
	if(ev->flag == SOCK_FLAG_READ || ev->flag == SOCK_FLAG_WRITE)
		ev->flag = SOCK_FLAG_NONE;
	kqueue_event_set(ev);
	return 0;
}

int event_update(event_t *ev, short flag, void *data, \
				void (*cb)(int, short, void *), struct timeval *tv)
{
	if(!ev)
		return -1;
	struct timeval zero = {0, 0};

	if(tv != NULL) {
		zero.tv_sec = tv->tv_sec;
		zero.tv_usec = tv->tv_usec;
	}
	event_del(ev);
	event_set(ev, ev->fd, flag, data, cb, &zero);
	debug_log(9, "fd %d, flag %d, data %p, cb %p, tv %p update\n",
					ev->fd, flag, data, cb, &zero);
	return event_add(ev);
}

void event_base_stop(event_base_t *base)
{
	kqueue_event_base_stop(base);
}

void event_base_clean(event_base_t *base)
{
	kqueue_event_base_clean(base);
}
#endif
